package com.tang.campus_market.ws;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tang.campus_market.entity.Message;
import com.tang.campus_market.entity.ResultMessage;
import com.tang.campus_market.util.MessageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {

    public static ChatEndpoint ChatEndpoint;
    @PostConstruct
    public void init() {
        ChatEndpoint = this;
        ChatEndpoint.redisTemplate = this.redisTemplate;
    }
    //redis对象
    @Autowired
    private RedisTemplate<String,? extends Object> redisTemplate;

    private ListOperations<String,ResultMessage> listOps;

    //用来存储每个用户客户端对象的ChatEndpoint对象
    private static Map<String,ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();

    //声明session对象，通过对象可以发送消息给指定的用户
    private Session session;

    //声明HttpSession对象，我们之前在HttpSession对象中存储了用户名
    private HttpSession httpSession;

    //连接建立
    @OnOpen
    public void onOpen(Session session, EndpointConfig config){
        //将局部session对象赋值给成员session
        this.session = session;
        //获取httpSession对象
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;

        //从httpSession对象中获取用户名
        String username = (String) httpSession.getAttribute("user");

        //存储登陆的对象到容器中
        onlineUsers.put(username,this);

        //将当前在线用户的用户名推送给所有的客户端
        //1 获取消息
        String message = MessageUtils.getMessage(true, null, getNames());
        //2 调用方法进行系统消息的推送
        broadcastAllUsers(message);

        //从Redis中拉取该用户所有未读消息
        listOps = (ListOperations<String, ResultMessage>) ChatEndpoint.redisTemplate.opsForList();
        List outlinemes = listOps.range(username, 0, -1);
        //将LinkedHashMap转化为JSON字符串
        String s = JSON.toJSONString(outlinemes);
        //将这个Json字符串转换为一个List集合
        List<ResultMessage> messages = JSONObject.parseArray(s, ResultMessage.class);
        if (messages != null && messages.size() > 0) {
            //调用方法进行离线消息推送
            ListIterator<ResultMessage> li = messages.listIterator();//获取ListIterator对象
            for (li = messages.listIterator(); li.hasNext();) {//将游标定位到列表结尾
                li.next();
            }
            for (; li.hasPrevious();) { //逆序输出集合中的信息
                ResultMessage message1 = li.previous();
                //清空离线消息
                listOps.rightPop(username);
                //获取推送给指定用户的消息格式的数据
                //获取消息数据
                String fromName = message1.getFromName();
                Object object = message1.getMessage();
                ObjectMapper mapper = new ObjectMapper();
                Message message2 = mapper.convertValue(object, Message.class);
                String data = message2.getMessage();

                //转化为Json字符串
                String resultMessage = MessageUtils.getMessage(false, fromName, data);
                //将消息发给离线消息的接收方
                broadcastToUser(username,resultMessage);
            }
        }

    }

    private void broadcastAllUsers(String message){
        try {
            //将消息推送给所有的客户端
            Set<String> names = onlineUsers.keySet();
            for (String name : names) {
                ChatEndpoint chatEndpoint = onlineUsers.get(name);
                chatEndpoint.session.getBasicRemote().sendText(message);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void broadcastToUser(String toName,String message){
        try {
            //将消息推送给该离线消息的接收方
            ChatEndpoint chatEndpoint = onlineUsers.get(toName);
            chatEndpoint.session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //返回在线用户名
    private Set<String> getNames(){
        return onlineUsers.keySet();
    }

    //接收到客户端发送的数据时被调用
    @OnMessage
    public void onMessage(String message,Session session){
        //将message数据转换成Message对象
        try {
            ObjectMapper mapper = new ObjectMapper();
            Message mess = mapper.readValue(message, Message.class);
            //获取要将数据发送给的用户
            String toName = mess.getToName();
            //获取消息数据
            String data = mess.getMessage();
            //获取当前登录的用户
            String username = (String) httpSession.getAttribute("user");
            //获取推送给指定用户的消息格式的数据
            String resultMessage = MessageUtils.getMessage(false, username, data);

            //如果要发送的用户在服务器上，直接发过去
            if (onlineUsers.containsKey(toName)) {
                //发送数据
                onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
            } else {//该用户目前没有连接在服务器上，发送到mysql或者redis
                //存到redis里
                ResultMessage result = new ResultMessage();
                result.setSystem(false);
                result.setFromName(username);
                result.setMessage(mess);

                Queue<ResultMessage> messageArrayList = new LinkedList<>();
                //offer方法添加失败不会报错
                messageArrayList.offer(result);

                listOps = (ListOperations<String, ResultMessage>) ChatEndpoint.redisTemplate.opsForList();

                listOps.leftPushAll(toName,messageArrayList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    //关闭
    @OnClose
    public void onClose(Session session) {

        String username = (String) httpSession.getAttribute("user");
        //从容器中删除指定的用户
        onlineUsers.remove(username);
        //获取推送的消息
        String message = MessageUtils.getMessage(true, null, getNames());
        broadcastAllUsers(message);
    }
}
